﻿/* 
 * Copyright 2010 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Reflection;
using Ngilead.Core.Exception;
using log4net;

namespace Ngilead.Core.Replicator
{
    /// <summary>
    /// Replicator for collections (IList, ...)
    /// </summary>
    public class CollectionReplicator : AbstractReplicator
    {
        #region Attributes

        /// <summary> Logger channel</summary>
        private static ILog logger = LogManager.GetLogger(typeof(CollectionReplicator));

        #endregion

        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        public CollectionReplicator()
        { }

        #endregion

        #region Public

        /// <summary>
        /// Replicate the argument collection, using the toClass for different class handling.
        /// </summary>
        public virtual T ReplicateIList<T>(T fromCollection) where T : IList
        {
            if (logger.IsInfoEnabled)
            {
                logger.Info(String.Format("ReplicateCollection<T>(fromCollection) : where T is {0}", typeof(T).ToString()));
            }

            T toCollection;
            try
            {
                toCollection = CreateToIList(fromCollection);

                // put TargetCloned
                PutTargetCloned(fromCollection, toCollection);

                // recursively populate member objects.
                foreach (Object fromMember in fromCollection)
                {
                    if (fromMember == null)
                    {
                        Type GType = fromCollection.GetType().GetGenericArguments()[0];
                        toCollection.Add(GType.IsValueType ? Activator.CreateInstance(GType) : null);
                    }
                    else
                    {
                        toCollection.Add(BeanTransformer.Replicate(fromMember));
                    }
                }
            }
            catch (System.Exception ex)
            {
                throw new CloneException(ex.Message, ex);
            }

            return toCollection;
        }

        ///// <summary>
        ///// Replicate the argument collection, using the toClass for different class handling.
        ///// </summary>
        //public virtual T ReplicateCollection<T>(T fromCollection) where T : ICollection
        //{
        //    if (logger.IsInfoEnabled)
        //    {
        //        logger.Info(String.Format("ReplicateCollection<T>(fromCollection) : where T is {0}", typeof(T).ToString()));
        //    }

        //    Type fromType = fromCollection.GetType();

        //    if (fromType.IsGenericType)
        //    {
        //        if (logger.IsInfoEnabled)
        //        {
        //            logger.Info("fromCollection.GetType().IsGenericType = true");
        //        }

        //        // generic invoke
        //        Type[] genericTypes = fromType.GetGenericArguments();
        //        MethodInfo methodInfo = typeof(CollectionReplicator).GetMethod("ReplicateGenericICollection", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(genericTypes);
        //        return (T) methodInfo.Invoke(this, new object[] { fromCollection });
        //    }
        //    else
        //    {
        //        throw new NotSupportedException("Unable to replicate collection");
        //    }
        //}
        
        #endregion

        #region Internal methods

        ///// <summary>
        ///// Replicate the argument generic collection
        ///// </summary>
        //protected ICollection<T> ReplicateGenericICollection<T>(ICollection<T> fromCollection)
        //{
        //    if (logger.IsInfoEnabled)
        //    {
        //        logger.Info(String.Format("ReplicateGenericCollection<T>(fromCollection) : where T is {0}", typeof(T).ToString()));
        //    }

        //    ICollection<T> toCollection;
        //    try
        //    {
        //        toCollection = CreateToGenericICollection<T>(fromCollection);

        //        // put TargetCloned
        //        PutTargetCloned(fromCollection, toCollection);

        //        // recursively populate member objects.
        //        foreach (Object fromMember in fromCollection)
        //        {
        //            if (fromMember == null)
        //            {
        //                toCollection.Add(default(T));
        //            }
        //            else
        //            {
        //                toCollection.Add((T)BeanTransformer.Replicate(fromMember));
        //            }
        //        }
        //    }
        //    catch (System.Exception ex)
        //    {
        //        throw new CloneException(ex.Message, ex);
        //    }

        //    return toCollection;
        //}

        ///// <summary>
        ///// Create a new (empty) collection of the same type as the argument
        ///// </summary>
        ///// <typeparam name="T">IList</typeparam>
        ///// <param name="from">The input collection</param>
        ///// <returns>The new empty collection</returns>
        //protected virtual ICollection<T> CreateToGenericICollection<T>(ICollection<T> from) 
        //{
        //    return CollectionUtil.CreateToGenericICollection<T>(from);
        //}

        /// <summary>
        /// Create a new (empty) collection of the same type as the argument
        /// </summary>
        /// <typeparam name="T">IList</typeparam>
        /// <param name="from">The input collection</param>
        /// <returns>The new empty collection</returns>
        protected virtual T CreateToIList<T>(T from) where T : IList
        {
            return CollectionUtil.CreateToIList(from);
        }

        #endregion
    }
}
